/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 dialog_file.cpp

	$Header: /resource_editor/dialog_file.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )


#include "dialog_file.h"

#include <fstream>
#include <io.h>
#include <time.h>
#include "adaptor_handler.h"
#include "bitmap_group_cache.h"
#include "bitmap_layer_window.h"
#include "blend_window.h"
#include "bound_handler.h"
#include "button.h"
#include "button_cache.h"
#include "compressed_filter.h"
#include "difficulty_level.h"
#include "directory_changer.h"
#include "external_string.h"
#include "interface_help.h"
#include "new_map_dialog.h"
#include "platform.h"
#include "replace_keywords.h"
#include "saved_game_header.h"
#include "scrollbar.h"
#include "simple_dialog.h"
#include "standard_fonts.h"
#include "text_window.h"
#include "toggle_button.h"
extern t_external_string const k_text_read_failed( "file.file_bad" );

extern t_button_cache       g_large_button;

inline bool operator<( t_file_dialog_data const& left, 
					   t_file_dialog_data const& right )
{
	if (left.is_folder != right.is_folder)
		return left.is_folder;
	if (left.is_folder)
		return stricmp( left.file_name.c_str(), right.file_name.c_str()) < 0;
	return left.file_time > right.file_time;
}

// ------------------------------------------------------------------
// save game dialog
// ------------------------------------------------------------------
t_dialog_file::t_dialog_file( t_bitmap_group_ptr bitmaps, t_window* parent )
                  : t_window( k_completely_transparent, parent )
{
	// load bitmaps
	t_screen_rect  rect;
	t_screen_rect  parent_rect = get_parent()->get_client_rect();

	m_bitmaps = bitmaps;
	rect = m_bitmaps->get_rect();
	rect -= rect.top_left();
	rect += (parent_rect.size() - rect.size()) / 2;
	init( rect );

	// create background
	t_bitmap_layer const* layer;
	t_window*             window;
	t_screen_point        zero(0,0);

	layer = m_bitmaps->find( "top" );
	window = new t_bitmap_layer_window( layer, zero, this );
	layer = m_bitmaps->find( "left" );
	window = new t_bitmap_layer_window( layer, zero, this );
	layer = m_bitmaps->find( "bottom" );
	window = new t_bitmap_layer_window( layer, zero, this );
	layer = m_bitmaps->find( "right" );
	window = new t_bitmap_layer_window( layer, zero, this );
	layer = m_bitmaps->find( "background" );
	m_background = new t_bitmap_layer_window( layer, zero, this );
	m_background_origin = -layer->get_rect().top_left();

	// create_buttons
	create_buttons( m_background, m_background_origin );

	// create scrollbar
	rect = m_bitmaps->find( "scrollbar_location" )->get_rect() + m_background_origin;
	m_scrollbar = new t_scrollbar( rect.top_left(), rect.height(), m_background, 0, 1 );
	m_scrollbar->set_handler( bound_handler( *this, &t_dialog_file::file_scroll ));
}

// ------------------------------------------------------------------
// base class for save game and load game dialogs.
// ------------------------------------------------------------------
void t_dialog_file::create_title( std::string const& title )
{
	t_text_window* text_window;
	t_screen_rect  rect;

	rect = m_bitmaps->find("title")->get_rect() + m_background_origin;
	text_window = new t_text_window( get_font( rect.height() ), rect, m_background,
		                             title, t_pixel_24(0,0,0));
	text_window->set_center_horizontal();
	text_window->set_drop_shadow( true, t_pixel_24(200,200,200));
}

// ------------------------------------------------------------------
// save game dialog
// ------------------------------------------------------------------
extern t_external_string const k_text_cancel;

void t_dialog_file::create_buttons( t_window* parent, t_screen_point origin )
{
	// create save and cancel buttons
	t_screen_rect  rect;
	t_screen_point point;
	t_help_block const&		shared_help = get_help_block( "shared" );
	t_help_block const&		help = get_help_block( "main_menu" );

	point = m_bitmaps->find( "cancel_location" )->get_rect().top_left() + origin;
	m_cancel_button = new t_button( g_large_button.get(), point, parent,  k_text_cancel );
	m_cancel_button->set_click_handler( bound_handler( *this, &t_dialog_file::cancel_click ));
	set_help( m_cancel_button, shared_help, "cancel" );
	
	// add buttons for file items
	int						i;
	int                     number;
	t_bitmap_layer const*	layer;
	t_window*               blend_window;
	t_window*               pressed_image;
	t_window*               released_image;
	std::string				layer_name;
	t_screen_rect			file_name_rect;
	t_screen_rect			file_time_rect;
	t_file_button           file_button;
	t_button_handler        handler;

	point = m_bitmaps->find( "line 01" )->get_rect().top_left();
	file_name_rect = m_bitmaps->find( "file_name" )->get_rect() - point;
	file_time_rect = m_bitmaps->find( "file_time" )->get_rect() - point;

	for (i = 0; i < (int)m_bitmaps->size(); i++)
	{
		layer = &(*m_bitmaps)[i];
		layer_name = layer->get_name();
		if (strnicmp( layer_name.c_str(), "line", 4 ) != 0)
			continue;

		// find the origin for this button
		point = layer->get_rect().top_left() + origin;
		file_button.button = new t_toggle_button( point, parent, true );
		// create a holding window for the released images
		released_image = new t_window( file_name_rect, k_completely_transparent, 
			                           file_button.button );
		file_button.file_name = new t_text_window( get_font( file_name_rect.height() ),
			                                       file_name_rect, released_image,
												   "", t_pixel_24( 0,0,0 ));
		file_button.file_name->set_drop_shadow( true, t_pixel_24(200,200,200));
		set_help( file_button.file_name, help, "load_game" );
		file_button.file_time = new t_text_window( get_font( file_time_rect.height() ),
			                                       file_time_rect, released_image,
												   "", t_pixel_24(0,0,0));
		file_button.file_time->set_drop_shadow( true, t_pixel_24(200,200,200));
		file_button.file_time->set_center_horizontal();
		// resize released image to reflect added rects
		released_image->set_size_from_children();
		file_button.button->set_released_image( released_image );

		// create a holding window to hold pressed images
		pressed_image = new t_window( file_name_rect, k_completely_transparent, 
			                          file_button.button );
		// create a blue background for the file name
		blend_window = new t_blend_window( t_pixel_24(0,0,200), 8, file_name_rect,
			                               pressed_image );
		file_button.highlighted_file_name = new t_text_window( get_font( file_name_rect.height() ),
                                                file_name_rect - file_name_rect.top_left(),
 											    blend_window, "", t_pixel_24( 255,255,255 ));
		file_button.highlighted_file_name->set_drop_shadow( true );
		set_help( file_button.highlighted_file_name, help, "load_game" );
		// create a blue background for the file time
		blend_window = new t_blend_window( t_pixel_24(0,0,200), 8, file_time_rect,
			                               pressed_image );
		file_button.highlighted_file_time = new t_text_window( get_font( file_time_rect.height() ),
			                                       file_time_rect - file_time_rect.top_left(),
												   blend_window, "", t_pixel_24(255,255,255));
		file_button.highlighted_file_time->set_drop_shadow( true );
		file_button.highlighted_file_time->set_center_horizontal();
		// update the holding window for the correct sizes
		pressed_image->set_size_from_children();
		file_button.button->set_pressed_image( pressed_image );
		number = m_file_buttons.size();
		handler = add_2nd_argument( bound_handler( *this, &t_dialog_file::file_selected ),
			                        number );
		file_button.button->set_click_handler( handler );
		handler = add_2nd_argument( bound_handler( *this, &t_dialog_file::double_click ),
			                         number );
		file_button.button->set_double_click_handler( handler );
		m_file_buttons.push_back( file_button );
		m_button_group.add( file_button.button );
	};
}

// ------------------------------------------------------------------
// save game dialog
// ------------------------------------------------------------------
void t_dialog_file::cancel_click( t_button* )
{
	m_return_value = 0;
	close();
}

// ------------------------------------------------------------------
// save game dialog
// ------------------------------------------------------------------
extern t_external_string const k_text_parent_folder;

void t_dialog_file::file_scroll( t_scrollbar* scrollbar, int top_line )
{
	int line;
	int index;

	line = m_selected_item - m_top_line;
	if (line >= 0 && line < (int)m_file_buttons.size())
		m_file_buttons[line].button->set_pressed( false );
	m_top_line = top_line;

	// set line items according to header data.
	line = 0;
	for (index = top_line; index < (int)m_files.size() && line < (int)m_file_buttons.size(); index++, 
		 line++)
	{
		t_file_button& button = m_file_buttons[line];
		t_file_data&   data   = m_files[index];
		std::string    file_name;
		std::string    file_time;

		file_name = data.file_name;
		file_time = data.file_time;

		if (data.is_folder)
		{
			if (strcmp( file_name.c_str(), ".." ) == 0)
				file_name = k_text_parent_folder;
			file_name = "[" + file_name + "]";
			file_time = "";
		}
		else
		{
			tm*     time_data;
			char	time_buffer[40];

			time_data = localtime( (time_t*)&data.file_time );
			strftime( time_buffer, sizeof(time_buffer), "%x %I:%M %p", time_data );
			file_time = time_buffer;
		}
		button.file_name->set_text( file_name );
		button.file_time->set_text( file_time );
		button.highlighted_file_name->set_text( file_name );
		button.highlighted_file_time->set_text( file_time );
		button.button->set_visible( true );
	}
	// hide extra line items
	while (line < (int)m_file_buttons.size())
	{
		m_file_buttons[line].button->set_visible( false );
		line++;
	}
	line = m_selected_item - m_top_line;
	if (line >= 0 && line < (int)m_file_buttons.size())
		m_file_buttons[line].button->set_pressed();
}

// ------------------------------------------------------------------
// save game dialog
// ------------------------------------------------------------------
void t_dialog_file::file_selected( t_button* button, int line )
{
	if (!button->is_pressed())
		return;
	if (m_top_line + line >= (int)m_files.size())
		return;
	m_selected_item = m_top_line + line;

	select_file( m_files[m_selected_item] );
}

// ------------------------------------------------------------------
// save game dialog
// ------------------------------------------------------------------
void t_dialog_file::double_click( t_button* button, int line )
{
	if (line + m_top_line >= (int)m_files.size())
		return;
	m_selected_item = m_top_line + line;

	t_file_data&   data   = m_files[m_selected_item];

	if (data.is_folder)
	{
		t_directory_changer changer( m_folder.c_str(), 0 );

		read_directory( data.file_name );
		return;
	}
	finish();
}

// ------------------------------------------------
// read files in directory
// ------------------------------------------------
void t_dialog_file::read_directory( std::string const& name )
{
	_finddata_t         file_data;
	int                 handle;
	t_saved_game_header header;
	t_file_data         file_entry;
	std::filebuf        file;
	char const*		    extension;
	t_directory_changer changer( name.c_str(), 0 );

	m_folder = get_current_folder();
	if (m_folder[m_folder.length()-1] != '\\')
		m_folder += '\\';

	// read map files
	handle = _findfirst( "*.*", &file_data );
	m_files.clear();
	if (handle != -1)
	{
		do
		{
			if (file_data.attrib & _A_SUBDIR)
			{
				if (strcmp( file_data.name, "." ) == 0)
					continue;
				file_entry.is_folder = true;
				file_entry.file_name = file_data.name;
				m_files.push_back( file_entry );
				continue;
			}

			extension = strrchr( file_data.name, '.' );
			if (extension == 0 || stricmp( extension, ".h4s" ) != 0)
				continue;

			if (file.open( file_data.name, std::ios::in | std::ios::binary ) == 0)
				continue;
			try
			{
				t_inflate_filter buffer( file );

				file_entry.is_folder = false;
				file_entry.file_name = file_data.name;
				file_entry.file_name = file_entry.file_name.substr( 0, extension - file_data.name );
				file_entry.file_time = file_data.time_write;
				if (read( buffer, header, 0 ))
				{
					file_entry.header = header;
					m_files.push_back( file_entry );
				}
			}
			catch( ...  )
			{
		        ok_dialog( replace_keywords(k_text_read_failed,"%file",file_data.name), true );
			}

			file.close();
		} while (_findnext( handle, &file_data ) == 0);
	}
	m_scrollbar->set_limits( 0, m_files.size() - m_file_buttons.size() );

	m_top_line = 0;
	m_selected_item = 0;
	std::sort( m_files.begin(), m_files.end() );
	m_scrollbar->set_position( 0 );
	file_scroll( 0, 0 );
}

// ---------------------------------------------------------------------------
// file information for saved games
// ---------------------------------------------------------------------------
int  const k_current_version = 47;
char const k_signature[] = "H4_SAVE_GAME";

bool read( std::streambuf& buffer, t_saved_game_header& header, t_progress_handler* handler )
{
	char signature[sizeof(k_signature)];

	read( buffer, signature, sizeof( signature ));
	if (memcmp( signature, k_signature, sizeof(signature) ) != 0)
		return false;
	header.version = get<int>( buffer );
	if ( header.version < 12 || header.version > k_current_version)
		return false;
	header.random_seed = get<int>( buffer );

	if ( header.version >= 30 )
	{
		if ( !header.map_file_ref.read( buffer ) )
			return false;
		header.map_number = get< t_uint16 >( buffer );
	}
	else
	{
		std::string file_name;
		if ( !read_string16( buffer, file_name ) )
			return false;
		header.map_file_ref = file_name;
		header.map_number = 0;
	}

	if ( header.version >= 31 )
		header.multi_scenario = get< t_uint8 >( buffer ) != 0;
	else
		header.multi_scenario = false;

	if ( !read_string16( buffer, header.map_name ) )
		return false;

	header.size = get<t_uint16>( buffer );
	header.levels = get<t_uint8>( buffer );
	header.map_random_seed = get<int>( buffer );

	header.map_difficulty = t_difficulty( get< t_uint8 >( buffer ) );

	if ( !::read_string16( buffer, header.map_description ) )
		return false;

	if ( header.version < 22 )
		header.player_difficulty = k_difficulty_easy;
    else if ( header.version < 29 )
    {
		header.player_difficulty = t_difficulty( get< t_uint8 >( buffer ) );
        // Fix bad data.
		header.player_difficulty = k_difficulty_easy;
    }
	else
		header.player_difficulty = t_difficulty( get< t_uint8 >( buffer ) );
	
    if ( header.version < 23 )
		header.turn_duration_index = 0;
	else
		header.turn_duration_index = get< t_uint8 >( buffer );
	if (header.version < 33)
		header.guards_can_move = true;
	else
		header.guards_can_move = get<t_uint8>( buffer ) != 0;
	
    return true;
}

// ---------------------------------------------------------------------------
// file information for saved games
// ---------------------------------------------------------------------------
bool write( std::streambuf& buffer, t_saved_game_header const& header )
{
	write( buffer, k_signature, sizeof( k_signature ));
	put<int>( buffer, k_current_version );
	put<int>( buffer, header.random_seed );
	if ( !header.map_file_ref.write( buffer ) )
		return false;
	put< t_uint16 >( buffer, header.map_number );
	put< t_uint8 >( buffer, header.multi_scenario );
	write_string16( buffer, header.map_name );
	put<t_uint16>( buffer, header.size );
	put<t_uint8>( buffer, header.levels );
	put<int>( buffer, header.map_random_seed );
	put<t_uint8>( buffer, header.map_difficulty );
	write_string16( buffer, header.map_description );
	put<t_uint8>( buffer, header.player_difficulty );
	put<t_uint8>( buffer, header.turn_duration_index );
	put<t_uint8>( buffer, header.guards_can_move );
	return true;
}
